package com.gitee.wsl.result

import com.gitee.wsl.result.sample.ProcessResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext

enum class ProcessState{
    INIT,LOADING
}

fun<P, R> CoroutineScope.createCommand(context: CoroutineContext = Dispatchers.Default, doWork:(params: P)-> R) = Command(context,doWork)

class Command<in P, R>(context: CoroutineContext,val block:(params: P)-> R):ResultInteractor<P, R>(context){
    override suspend fun doWork(params: P) = block(params)
}


abstract class ResultInteractor<in P, R>(
    private val context: CoroutineContext
) {
    @Throws(Exception::class)
    fun asFlow(params: P): Flow<R> = flow { emit(doWork(params)) }.flowOn(context)

    fun stream(params: P): Flow<StateEntryResult<R,Throwable,ProcessState>> {
        return flow<StateEntryResult<R,Throwable,ProcessState>> {
            emit(ProcessResult.StateValue(ProcessState.LOADING))
            val r = doWork(params)
            emit(ProcessResult.Ok(r))
        }.catch { t ->
            emit(ProcessResult.Err(t))
        }.flowOn(context)
    }

    suspend fun run(params: P) = doWork(params)

    /*
    * N.B. Executes on the caller's thread.
    * */
    suspend fun execute(params: P): EntryResult<R,Throwable> = runCatching { doWork(params) }

    suspend fun async(params: P): EntryResult<R,Throwable> = withContext(context) {
        runCatching { doWork(params) }
    }

    protected abstract suspend fun doWork(params: P): R
}